import type { Aoe, IValues } from 'aoe';
import type { Ref } from 'vue';
import { computed } from 'vue';
import { obj_update } from 'aoe';

export type FnUpdate<T> = (item: T, value: unknown, origin?: IValues) => IValues | undefined;

export type FnFilter<T> = (items?: T[], origin?: IValues) => T[] | undefined;

export type FnValue<T> = (items: T) => unknown;

export interface ElasticGroupProps<T extends Aoe = Aoe> {
    /**
     * 事件-数据更新
     */
    fnUpdate?: FnUpdate<T>;
    /**
     * 函数-元素过滤
     */
    fnFilter?: FnFilter<T>;
    /**
     * 函数-元素默认值
     */
    fnValue?: FnValue<T>;
    /**
     * 索引-据此在数据对象中查找元素的值
     */
    itemKey?: string;
}

export interface GroupProps<T extends Aoe = Aoe> extends ElasticGroupProps<T> {
    /**
     * 数组-循环的元素
     */
    items?: T[];
}

export const update_group_value = <T extends Aoe = Aoe>(
    field: T,
    v?: unknown,
    values?: IValues,
    key: keyof T = 'name' as keyof T,
) => (!field[key] ? values : obj_update(values, field[key] as string, v));

export const useElasticGroup = <T extends Aoe = Aoe>(
    modelValue: Ref<unknown>,
    props: GroupProps<T>,
) => {
    const key = computed(() => (props.itemKey ?? 'name') as keyof T);

    const fn = computed<FnUpdate<T>>(
        () =>
            props.fnUpdate ?? ((item, v, values) => update_group_value(item, v, values, key.value)),
    );

    const update = (item: T, v: unknown) =>
        (modelValue.value = fn.value(item, v, modelValue.value as IValues));

    const getValue = (item?: T) => {
        if (!item?.[key.value]) return undefined;

        return (modelValue.value as IValues)?.[item[key.value] as string] ?? props.fnValue?.(item);
    };

    return { update, getValue };
};

export const useGroup = <T extends Aoe = Aoe>(modelValue: Ref<unknown>, props: GroupProps<T>) => {
    const { update, getValue } = useElasticGroup(modelValue, props);
    const items_ = computed(
        () => props.fnFilter?.(props.items, modelValue.value as IValues) ?? props.items,
    );
    return { update, items_, getValue };
};
